package cc.rengu.igas.route.core.realize.impl;

import cc.rengu.igas.route.common.constant.SmartRouteParamConstant;
import cc.rengu.igas.route.common.dao.*;
import cc.rengu.igas.route.common.dao.impl.*;
import cc.rengu.igas.route.common.entity.MultiRatioWeightCfg;
import cc.rengu.igas.route.common.entity.RuleCfg;
import cc.rengu.igas.route.common.entity.WeightCfg;
import cc.rengu.igas.route.common.enums.RouteRuleTypeEnum;
import cc.rengu.igas.route.core.realize.RouterDataInit;
import cc.rengu.oltp.service.common.constant.AppConfigConstant;
import cc.rengu.oltp.service.common.constant.AppParamConstant;
import cc.rengu.oltp.service.common.entity.DstTxnAuthCfg;
import cc.rengu.oltp.service.common.entity.ChannelCallCfg;
import cc.rengu.oltp.utility.util.RedisUtil;
import cc.rengu.oltp.utility.util.StringUtil;
import cc.rengu.oltp.utility.util.XmlConfigUtil;
import cc.rengu.utility.log.RgLog;
import cc.rengu.utility.log.RgLogger;
import com.alibaba.fastjson.JSON;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 加载多路比例路由配置表
 * 1. 加载缓存的原则为数据库中所有数据都加载缓存，因为是静态配置数据也不会存在数据量太大的问题
 * 2. 基于以上原则肯定存在终态的问题，因此所有数据都需要判断终态
 * 3. 由于存在集群的问题，不需要每台服务器加载都重新覆盖，因此在redis中设置一个标志，该标志存在有效期24小时
 * 4. 若以上标志存在则不加载，若不存在则加载
 * 5. 由于数据库中数据都不会删除只会设置状态为失效，因此可以不用处理缓存中存在而数据库中不存在数据的情况
 *
 * @author xionglz
 * @version 1.0
 * @date 2020-03-23
 */
public class RouterConfigDataInit implements RouterDataInit {
    private RgLogger rglog = RgLog.getLogger(this.getClass().getName());

    @Override
    public int loadRouterData() throws Exception {
        rglog.info("路由加载开始");
        String redisDataLoadFlag = RedisUtil.onceGet(SmartRouteParamConstant.ROUTER_REDIS_DATA_LOAD_FLAG);
        if (null != redisDataLoadFlag && !redisDataLoadFlag.isEmpty()) {
            String redisForceRefreshFlag = XmlConfigUtil.getXmlConfigNodeAttrValue(AppConfigConstant.REDIS_CONF_NODE, AppConfigConstant.REDIS_FORCE_REFRESH_FLAG);
            if (!Boolean.parseBoolean(redisForceRefreshFlag)) {
                rglog.info("Redis已加载智能路由相关数据，不再重新加载!");
                return 0;
            }
        }
        return reloadAll();
    }

    private int reloadAll() throws Exception {
        //开启游标查询所有智能路由规则配置表
        RuleCfgMapper ruleCfgMapper = new RuleCfgMapperImpl();
        List<RuleCfg> listRuleCfg = ruleCfgMapper.getAllRuleCfg();
        if (null == listRuleCfg || listRuleCfg.isEmpty()) {
            rglog.info("智能路由配置表无数据，无需加载！");
            return 0;
        }
        RedisUtil.onceSet(SmartRouteParamConstant.ROUTER_REDIS_DATA_LOAD_FLAG, "true", 86400);
        RedisUtil.onceDel(SmartRouteParamConstant.ROUTE_CONF_RULE);
        RedisUtil.onceDel(SmartRouteParamConstant.ROUTE_CONF_STATIC);
        RedisUtil.onceDel(SmartRouteParamConstant.ROUTE_CONF_MULTI);
        RedisUtil.onceDel(SmartRouteParamConstant.ROUTE_CONF_SMART);
        RedisUtil.onceDel(SmartRouteParamConstant.ROUTE_CONF_PRIO);
        RedisUtil.onceDel(SmartRouteParamConstant.ROUTE_CONF_TXN_NUM_MAP);
        RedisUtil.onceDel(SmartRouteParamConstant.ROUTE_CONF_DST_TXN_AUTH);
        //遍历路由配置表
        for (RuleCfg ruleCfg : listRuleCfg) {
            if (null == ruleCfg) {
                rglog.error("过滤空数据");
                continue;
            }
            loadRouteCfg(ruleCfg);
            try {
                //静态路由-加载比例路由信息表到缓存
                if (RouteRuleTypeEnum.STATIC.getCode().equals(ruleCfg.getRuleType())) {
                    if (0 != loadStatic(ruleCfg)) {
                        throw new Exception("加载静态路由配置错误");
                    }
                    rglog.info("静态路由数据加载成功");
                }
                //比例路由-加载比例路由信息表到缓存
                else if (RouteRuleTypeEnum.MULTI_RATE.getCode().equals(ruleCfg.getRuleType())) {
                    if (0 != loadMulti(ruleCfg)) {
                        throw new Exception("加载比例路由配置错误");
                    }
                    rglog.info("多路比例路由数据加载成功");
                }
                //智能路由-加载智能路由信息到缓存
                else if (RouteRuleTypeEnum.SMART.getCode().equals(ruleCfg.getRuleType())) {
                    if (0 != loadSmart(ruleCfg)) {
                        throw new Exception("加载智能路由配置错误");
                    }
                    rglog.info("智能路由数据加载成功");
                } else {
                    throw new Exception("配置错误,不支持的路由类型" + ruleCfg.getRuleType());
                }
            } catch (Exception e) {
                rglog.error(e.getMessage());
                RedisUtil.onceDel(SmartRouteParamConstant.ROUTER_REDIS_DATA_LOAD_FLAG);
                String routeCfgKey = SmartRouteParamConstant.ROUTE_CONF_RULE + ":" + ruleCfg.getInstId() + ":" + ruleCfg.getSrcChannelId() + ":" + ruleCfg.getTransChannelId() + ":" + ruleCfg.getTxnGroup();
                RedisUtil.onceDel(routeCfgKey);
                return -1;
            }
        }
        rglog.info("路由加载成功");

        //加载路由客户化的交易码映射
        loadChannelCallCfgRouteCustom();

        //加载路由客户化的通道交易权限信息
        loadDstTxnAuthRouteCustom();

        return 0;
    }

    public int reloadByInst(String instId) throws Exception {
        if (StringUtil.isEmptyOrNull(instId) || AppParamConstant.DEFAULT_INSTID.equals(instId)) {
            rglog.error("必须上送机构,且机构不能为默认机构");
            return -1;
        }
        //开启游标查询所有智能路由规则配置表
        RuleCfgMapper ruleCfgMapper = new RuleCfgMapperImpl();
        List<RuleCfg> listRuleCfg = ruleCfgMapper.getAllRuleCfg();
        if (null == listRuleCfg || listRuleCfg.isEmpty()) {
            rglog.info("智能路由配置表无数据，无需加载！");
            return 0;
        }
        RedisUtil.onceSet(SmartRouteParamConstant.ROUTER_REDIS_DATA_LOAD_FLAG, "true", 86400);
        //遍历路由配置表
        for (RuleCfg ruleCfg : listRuleCfg) {
            if (null == ruleCfg) {
                rglog.error("过滤空数据");
                continue;
            }
            if (!ruleCfg.getInstId().equals(instId)) {
                continue;
            }
            loadRouteCfg(ruleCfg);
            try {
                //静态路由-加载比例路由信息表到缓存
                if (RouteRuleTypeEnum.STATIC.getCode().equals(ruleCfg.getRuleType())) {
                    if (0 != loadStatic(ruleCfg)) {
                        throw new Exception("加载静态路由配置错误");
                    }
                    rglog.info("静态路由数据加载成功");
                }
                //比例路由-加载比例路由信息表到缓存
                else if (RouteRuleTypeEnum.MULTI_RATE.getCode().equals(ruleCfg.getRuleType())) {
                    if (0 != loadMulti(ruleCfg)) {
                        throw new Exception("加载比例路由配置错误");
                    }
                    rglog.info("多路比例路由数据加载成功");
                }
                //智能路由-加载智能路由信息到缓存
                else if (RouteRuleTypeEnum.SMART.getCode().equals(ruleCfg.getRuleType())) {
                    if (0 != loadSmart(ruleCfg)) {
                        throw new Exception("加载智能路由配置错误");
                    }
                    rglog.info("智能路由数据加载成功");
                } else {
                    throw new Exception("配置错误,不支持的路由类型" + ruleCfg.getRuleType());
                }
            } catch (Exception e) {
                rglog.error(e.getMessage());
                RedisUtil.onceDel(SmartRouteParamConstant.ROUTER_REDIS_DATA_LOAD_FLAG);
                String routeCfgKey = SmartRouteParamConstant.ROUTE_CONF_RULE + ":" + ruleCfg.getInstId() + ":" + ruleCfg.getSrcChannelId() + ":" + ruleCfg.getTransChannelId() + ":" + ruleCfg.getTxnGroup();
                RedisUtil.onceDel(routeCfgKey);
                return -1;
            }
        }
        rglog.info("路由加载成功");

        //加载路由客户化的交易码映射
        loadChannelCallCfgRouteCustom();

        //加载路由客户化的通道交易权限信息
        loadDstTxnAuthRouteCustom();

        return 0;
    }

    private void loadChannelCallCfgRouteCustom() throws Exception {
        //智能路由的相关渠道交易信息及通道交易信息的配置，该部分数据OLTP未加载，需要智能路由自身完成加载
        rglog.info("路由交易映射加载开始");
        //交易码映射信息按照MSG_SRC_ID为IGRT加载
        ChannelCallCfgRouteCustomMapper ChannelCallCfgMapper = new ChannelCallCfgRouteCustomMapperImpl();
        List<ChannelCallCfg> ChannelCallCfgList = ChannelCallCfgMapper.selectChannelCallCfgInfoByMsgSrcId(SmartRouteParamConstant.IGRT);
        String routeTxnNumKey;
        Map<String, List<ChannelCallCfg>> ChannelCallCfgGroupMap = new HashMap<>();
        if (null != ChannelCallCfgList && !ChannelCallCfgList.isEmpty()) {
            //先按照法人及渠道交易分组
            for (ChannelCallCfg channelCallCfg : ChannelCallCfgList) {
                String key = channelCallCfg.getInstId() + ":" +
                        channelCallCfg.getMsgSrcId() + ":" + channelCallCfg.getTxnNum() + ":" + channelCallCfg.getBizType();
                List<ChannelCallCfg> ChannelCallCfgListTmp = ChannelCallCfgGroupMap.get(key);
                if (null == ChannelCallCfgListTmp) {
                    List<ChannelCallCfg> ChannelCallCfgs = new ArrayList<>();
                    ChannelCallCfgGroupMap.put(key, ChannelCallCfgs);
                    ChannelCallCfgs.add(channelCallCfg);
                } else {
                    ChannelCallCfgListTmp.add(channelCallCfg);
                }
            }
            //渠道都为IGRT即每种业务（代收、代付、鉴权还包括每种业务的其他支撑类的交易）支持的通道
            for (List<ChannelCallCfg> ChannelCallCfgs : ChannelCallCfgGroupMap.values()) {
                if (ChannelCallCfgs.isEmpty()) {
                    continue;
                }
                ChannelCallCfg channelCallCfg = ChannelCallCfgs.get(0);
                if (null == channelCallCfg) {
                    continue;
                }
                routeTxnNumKey = SmartRouteParamConstant.ROUTE_CONF_TXN_NUM_MAP + ":" + channelCallCfg.getInstId() + ":" +
                        channelCallCfg.getMsgSrcId() + ":" + channelCallCfg.getTxnNum() + ":" + channelCallCfg.getBizType();
                RedisUtil.onceSet(routeTxnNumKey, JSON.toJSONString(ChannelCallCfgs));
                rglog.info("交易码映射加载key:<{}>", routeTxnNumKey);
            }
        } else {
            //当前不支持智能路由，继续启动，后续可能动态加入数据
            rglog.warn("当前不支持智能路由，继续启动，后续可能动态加入数据");
        }
        rglog.info("路由交易映射加载成功");
    }

    private void loadDstTxnAuthRouteCustom() throws Exception {
        rglog.info("路由通道交易权限加载开始");
        //通道交易权限信息按照是否支持智能路由为Y加载，该部分可能和OLTP重复加载
        DstTxnAuthCfgRouteCustomMapper dstTxnAuthCfgMapper = new DstTxnAuthCfgRouteCustomMapperImpl();
        List<DstTxnAuthCfg> dstTxnAuthCfgList = dstTxnAuthCfgMapper.selectDstTxnAuthCfgInfoByRouteFlag();
        String dstTxnAuthKey;
        if (null != dstTxnAuthCfgList && !dstTxnAuthCfgList.isEmpty()) {
            for (DstTxnAuthCfg dstTxnAuthCfg : dstTxnAuthCfgList) {
                dstTxnAuthKey = SmartRouteParamConstant.ROUTE_CONF_DST_TXN_AUTH + ":" + dstTxnAuthCfg.getInstId() + ":" +
                        dstTxnAuthCfg.getMsgDstId() + ":" + dstTxnAuthCfg.getChannelTxn();
                RedisUtil.onceSet(dstTxnAuthKey, JSON.toJSONString(dstTxnAuthCfg));
                rglog.info("通道交易权限加载key:<{}> <{}>", dstTxnAuthKey, dstTxnAuthCfg.getTxnName());
            }
        } else {
            //当前不支持智能路由，继续启动，后续可能动态加入数据
            rglog.warn("当前不支持智能路由，继续启动，后续可能动态加入数据");
        }
        rglog.info("路由通道交易权限加载成功");
    }

    /**
     * 加载指定的路由入口配置
     *
     * @param ruleCfg 路由配置入口
     */
    public void loadRouteCfg(RuleCfg ruleCfg) throws Exception {
        if (null == ruleCfg) {
            rglog.warn("未配置路由规则");
            return;
        }
        String routeCfgKey = SmartRouteParamConstant.ROUTE_CONF_RULE + ":" + ruleCfg.getInstId() + ":" + ruleCfg.getSrcChannelId() + ":" + ruleCfg.getTransChannelId() + ":" + ruleCfg.getTxnGroup();
        RedisUtil.onceSet(routeCfgKey, JSON.toJSONString(ruleCfg));
        rglog.debug("<key:{}>,已加载到缓存", routeCfgKey);
    }

    /**
     * 智能路由配置加载
     *
     * @param ruleCfg 路由入口配置
     * @return 0成功 非0配置错误的失败需要先配置正确后再启动
     * @throws Exception 异常
     */
    public int loadSmart(RuleCfg ruleCfg) throws Exception {
        if (null == ruleCfg) {
            rglog.warn("未配置智能路由规则");
            return 0;
        }
        WeightCfgMapper weightCfgMapper = new WeightCfgMapperImpl();
        //查询智能路由权重配置表
        WeightCfg weightCfg = weightCfgMapper.getWeightCfgList(ruleCfg.getInstId(), ruleCfg.getRuleId());
        if (weightCfg == null) {
            rglog.error("智能路由<inId:{}>,<ruleId:{}>,未查询到智能路由权重配置", ruleCfg.getInstId(), ruleCfg.getRuleId());
            return -1;
        }
        int weightTotal = weightCfg.getFeeCostWeight() + weightCfg.getStressWeight() + weightCfg.getSuccRateWeight() + weightCfg.getTimeCostWeight();
        if (SmartRouteParamConstant.INT_ONE_HUNDRED != weightTotal) {
            rglog.error("智能路由<inId:{}>,<ruleId:{}>,<weightTotal:{}>，权重不等于100", ruleCfg.getInstId(), ruleCfg.getRuleId(), weightTotal);
            return -1;
        }
        //拼接智能路由权重key
        String key = SmartRouteParamConstant.ROUTE_CONF_SMART + ":" + weightCfg.getInstId() + ":" + weightCfg.getRuleId();
        //直接设置包括覆盖
        RedisUtil.onceSet(key, JSON.toJSONString(weightCfg));
        rglog.debug("智能路由<key:{}>,已经设置到缓存", key);
        return 0;
    }

    /**
     * 静态路由加载
     *
     * @param ruleCfg 路由入口配置
     * @return 0成功 非0配置错误的失败需要先配置正确后再启动
     * @throws Exception 异常
     */
    public int loadStatic(RuleCfg ruleCfg) throws Exception {
        if (null == ruleCfg) {
            rglog.warn("未配置静态路由规则");
            return 0;
        }
        String key = SmartRouteParamConstant.ROUTE_CONF_STATIC + ":" + ruleCfg.getInstId() + ":" + ruleCfg.getRuleId();
        MultiRatioWeightCfgMapper multiRatioWeightCfgMapper = new MultiRatioWeightCfgMapperImpl();
        //根据rule_id查询比例路由配置表
        List<MultiRatioWeightCfg> listMultiRatioWeightCfg = multiRatioWeightCfgMapper.getMultiRatioWeightCfg(ruleCfg.getInstId(), ruleCfg.getRuleId());
        if (null != listMultiRatioWeightCfg && listMultiRatioWeightCfg.size() != 0) {
            //静态路由只有一条数据
            if (listMultiRatioWeightCfg.size() != 1) {
                rglog.error("静态路由<size:{}>,静态路由配置数据条数错误,<ruleId:{}>", listMultiRatioWeightCfg.size(), ruleCfg.getRuleId());
                return -1;
            }
            if (SmartRouteParamConstant.INT_ONE_HUNDRED != listMultiRatioWeightCfg.get(0).getWeight()) {
                rglog.error("静态路由<weight:{}>，静态路由权重配置错误,<ruleId:{}>", listMultiRatioWeightCfg.get(0).getWeight(), ruleCfg.getRuleId());
                return -1;
            }
            MultiRatioWeightCfg multiRatioWeightCfg = listMultiRatioWeightCfg.get(0);
            RedisUtil.onceSet(key, JSON.toJSONString(multiRatioWeightCfg));
            rglog.debug("静态路由<key:{}>,已加载到缓存", key);
        } else {
            rglog.debug("静态路由配置错误！配置了路由入口规则<{}>但是没有配置实际的静态路由数据", ruleCfg.getRuleId());
            return -1;
        }
        return 0;
    }

    /**
     * 多路比例路由加载
     *
     * @param ruleCfg 路由入口配置
     * @return 0成功， 非0配置错误失败，需要配置正确后再启动
     * @throws Exception 异常
     */
    public int loadMulti(RuleCfg ruleCfg) throws Exception {
        if (null == ruleCfg) {
            rglog.warn("未配置多路比例路由规则");
            return 0;
        }
        //根据rule_id查询比例路由配置表
        MultiRatioWeightCfgMapper multiRatioWeightCfgMapper = new MultiRatioWeightCfgMapperImpl();
        List<MultiRatioWeightCfg> listMultiRatioWeightCfg = multiRatioWeightCfgMapper.getMultiRatioWeightCfg(ruleCfg.getInstId(), ruleCfg.getRuleId());
        if (null != listMultiRatioWeightCfg) {
            if (listMultiRatioWeightCfg.size() < SmartRouteParamConstant.INT_TWO) {
                rglog.error("多路比例路由<size:{}>,多路比例路由配置数据条数错误,<ruleId:{}>", listMultiRatioWeightCfg.size(), listMultiRatioWeightCfg.get(0).getRuleId());
                return -1;
            }
            //遍历求和总权重
            int weightTotal = 0;
            for (MultiRatioWeightCfg multiRatioWeightCfg : listMultiRatioWeightCfg) {
                weightTotal = weightTotal + multiRatioWeightCfg.getWeight();
            }
            if (SmartRouteParamConstant.INT_ONE_HUNDRED != weightTotal) {
                rglog.error("多路比例路由<weightTotal:{}>,多路比例路由配置总权重不等100,<ruleId:{}>", weightTotal, listMultiRatioWeightCfg.get(0).getRuleId());
                return -1;
            }
            String key = SmartRouteParamConstant.ROUTE_CONF_MULTI + ":" + ruleCfg.getInstId() + ":" + ruleCfg.getRuleId();
            RedisUtil.onceSet(key, JSON.toJSONString(listMultiRatioWeightCfg));
            rglog.debug("多路比例路由<key:{}>,已加载到缓存", key);
        } else {
            rglog.debug("多路比例路由配置错误！配置了路由入口规则<{}>但是没有配置实际的多路比例路由数据", ruleCfg.getRuleId());
            return -1;
        }
        return 0;
    }
}
